#ifndef __FAT32_H
#define __FAT32_H

#include "globals.h"

#define INVALID_CLUSTER	0xFFFFFFFF
#define SECTOR_SIG 510
#define MAX_FILENAME_LEN 260

typedef int (*fs_read) (uint32_t sector, unsigned char *buffer);

typedef struct sector_buf_s
{
	// Sector number
	uint32_t address;

	// Full sector buffer
	uint8_t buf[512];

} sector_buf_t;

typedef struct fat_io_s
{
	fs_read		read_sector;

	sector_buf_t sector_buffer;
} fat_io_t;


typedef struct fs_loc
{
	uint32_t FAT_start_sector; //Where the 1st File Alloc Table begins

	uint8_t  FAT_count;

	uint8_t entries_per_fat_sector;

	uint32_t root_cluster; //First cluster of root director (usually 2)

	uint32_t root_sectors_count; // Number of sectors comprising the root directory

	uint32_t root_cluster_count; // Number of clusters comprising the root directory

	uint32_t data_start_sector; // First sector of the data portion of volume

	uint16_t bytes_per_sector;

	uint8_t	sectors_per_cluster;

	uint32_t sectors_per_fat;
} fs_loc_t;

typedef struct fatfs_s
{
	uint8_t  type_code;
	uint32_t lba_begin;
	uint32_t sector_count;
} fatfs_t;

typedef struct fatvol_s
{
	uint32_t fat_lba_begin; // Sector where File Alloc Table begins

	uint32_t fat_cluster_begin; // where cluster 2 (which is the root dir cluster) starts...this
								//takes into account File Alloc Tables. We use it to index into
								//the data section in the partition using the cluster nums in the FAT
	uint32_t root_sector_count;

	uint16_t	bytes_per_sector; // Num of bytes per sector, commonly 512
	uint8_t 	sectors_per_cluster; // Allowed vales are powers of 2 from 1 to 128
	uint16_t	reserved_sector_count; // Num sectors before first FAT, min of 1 (for this sector)
									// Usually 32 for FAT32
	uint8_t		num_of_fat;	// Num of file allocation tables -> almost always 2, RAM disk might use 1
	uint16_t	root_dir_entries; // Must be 0 for FAT32...used only be FAT12/FAT16

	uint8_t		media_desc; // Media descriptor

	uint16_t	sectors_per_track; // Used for media with geometry

	uint32_t	num_hidden_sectors; // Num of hidden sectors
	uint32_t	total_sectors; // Num of sectors on volume (count of sec in all 4 regions) Must Be NON ZERO
	uint32_t	sector_per_fat; // Num of sectors per ONE FAT
	uint16_t	ext_flags;	// bit[0:3] = (zero-based) num of active FAT. Only valid if mirroring disabled
							// bit[4:6] = reserved
							// bit[7] = (0 = fat mirrored at runtime into all fats) (1=only 1 fat active->referenced in bit[0:3]
							// bit[8:15] = reserved
	uint32_t	root_cluster; // Cluster number of first cluster in root directory (usually 2)
	uint16_t	fs_info; // Sector number of FSINFO structure (usually 1)
	uint16_t	boot_sec_backup; // If non-zero indicates sector number of boot record backup

	uint8_t		reserved_1;
	uint8_t		boot_sig; // extended boot signature (0x29), indicates next three fields are present
	uint32_t	id;		// Indicates the volumes unique id
	uint8_t		name[11]; // String indicating FAT type (Cannot be used to test for type...this is just a string)
	uint8_t		fs_type[8]; //Always set to "FAT32  "...cannot be used for type determination

	uint16_t	boot_sector_signature; // Found at byte offset (0x1FE) Always must be 0xAA55
} fatvol_t;

typedef struct fatfs_info_s
{
	uint8_t	fs_info_sig_start[4]; // FSINFO signature (0x52,0x52,0x61,0x41) ("RRaA")
	// then follows 480 bytes of reserved space
	uint8_t	fs_info_sig_middle[4]; // FSINFO signature (0x72,0x72,0x41,0x61) ("rrAa")
	uint32_t free_sectors; // Number of free sectors on drive (-1) if unknown
	uint32_t last_cluster_alloc; // Number of most recently allocated cluster
	uint8_t reserved[12]; // Reserved
	uint8_t fs_info_sig_end[4]; // FSINFO signature (0x00,0x00,0x55,0xAA)
} fatfs_info_t;

typedef struct fs_s
{
	// Boolean value indicating if fs is initalized
	volatile uint8_t is_initalized;

	// Struct to contain read/write functions
	fat_io_t fat_io;

	// Struct containing MBR info
	fatfs_t mbr;

	// Struct containing fat_info
	fatfs_info_t info;

	// Struct locations (basic volume layout info)
	fs_loc_t loc;

	uint32_t current_song_num;

} fs_t;

struct fatfs_dir_entry
{
	unsigned char Name[11];
	unsigned char Attr;
	unsigned char NTRes;
	unsigned char CrtTimeTenth;
	unsigned char CrtTime[2];
	unsigned char CrtDate[2];
	unsigned char LstAccDate[2];
	uint16_t FstClusHI;
	unsigned char WrtTime[2];
	unsigned char WrtDate[2];
	uint16_t FstClusLO;
	uint32_t FileSize;
};

typedef enum FAT_FILE_FLAGS_e
{
	FAT_FILE_GOOD = 0,
	FAT_FILE_ERR,
	FAT_FILE_EOF
} FAT_FILE_STATUS_t;

typedef struct FAT_FILE_s
{
	uint32_t	start_cluster; // FAT Based cluster index number
	uint32_t	current_cluster; // FAT Based current buffered sector index
	uint32_t	cluster_index;
	uint16_t	sector_index;
	uint32_t	byte_index;

	uint8_t		filename[12];	// 8.3 Fat Filename + null terminator
	uint32_t	size;
	uint8_t		buf[512];		// Sector buffer

	FAT_FILE_STATUS_t flags;

} FAT_FILE_t;


/****************************************************************************
* Purpose:	Counts the number of mp3 files in the root directory
* Input:	No Input
* Output:   Returns the number of mp3 files in root dir
****************************************************************************/
uint16_t fat_num_of_mp3_file(void);

/****************************************************************************
* Purpose:	Buffers in the next sector in file
* Input:	Valid fileptr
* Output:   No Output
****************************************************************************/
void fat_file_buffer_next_sector(FAT_FILE_t * fp);

/****************************************************************************
* Purpose:	Finds the next cluster in cluster chain via current cluster index
* Input:	Current cluster index number
* Output:   Next cluster index number or End of Cluster Chain value
****************************************************************************/
uint32_t fat_find_next_cluster(uint32_t current_cluster);

/****************************************************************************
* Purpose:	Low level file open function
* Input:	Valid fileptr with filename pre-loaded
* Output:   Return 0 on success, -1 on failure
****************************************************************************/
int fat_open_file(FAT_FILE_t *fp);

/****************************************************************************
* Purpose:	Gets a fat_vol info sector and populates a vol structure with
*			it's data
* Input:	Valid vol record pointer
* Output:   No Output
****************************************************************************/
void populate_vol_info(fatvol_t * vol);

/****************************************************************************
* Purpose:	Converts a location in local buffer to int whose width is
*			specified by "count" and places it in dest ptr"buf"
* Input:	Index into buffer value, width of integer (must be power of 2),
			and a voidptr to valid data of equal width
* Output:   No Output
****************************************************************************/
void convert2int(uint32_t begin, uint32_t count, void* buf);

/****************************************************************************
* Purpose:	Converts a location in local buffer to int whose width is
*			specified by "count"
* Input:	pointer to location in buffer from which to grab value, width of
*			integer (must be power of 2), and a voidptr to valid data of
*			equal width
* Output:   No Output
****************************************************************************/
void convertbuf2int(void * buf, uint32_t count, void* dest);

/****************************************************************************
* Purpose:	Closes a file. Free fileptr memory
* Input:	No Input
* Output:   No Output
****************************************************************************/
void fat_fclose(FAT_FILE_t * fp);

/****************************************************************************
* Purpose:	Reads up to "num_bytes" from fileptr "fp" and places data in
*			"buf"
* Input:	A destination buffer "buf", the number of bytes to read
*			"num_bytes" and the fileptr "fp"
* Output:   Returns the number of bytes read
****************************************************************************/
int	fat_fread(void* buf, uint16_t num_bytes, FAT_FILE_t * fp);

/****************************************************************************
* Purpose:	Initalizes the fat filesystem
* Input:	No Input
* Output:   Returns a value from ERROR_CODES enumeration indicating fatfs
*			status (INIT_SUCCESS on success)
****************************************************************************/
int fatfs_init(fs_read in);

/****************************************************************************
* Purpose:	Disables the fat lib
* Input:	No Input
* Output:   No Output
****************************************************************************/
void fatfs_disable(void);

/****************************************************************************
* Purpose:	Opens a file
* Input:	Short filename (8.3 Filename supported only)
* Output:   Returns a fileptr if file is found, or NULL if not found
****************************************************************************/
FAT_FILE_t * fat_fopen(uint8_t * filename);

/****************************************************************************
* Purpose:	Lists all the files in the root dir
* Input:	No Input
* Output:   Not Output
****************************************************************************/
void fat_ls_root_dir(void);

/****************************************************************************
* Purpose:	Seeks to Beginning or End minus offset (not a true implementation
*			of fseek)
* Input:	Valid Fileptr, offset value (if seeking to end), and which end to
*			seek to (1 = END, 0 = BEGIN)
* Output:   Returns 0 on success, non-zero on failure
****************************************************************************/
int fat_fseek(FAT_FILE_t *fp, uint32_t offset, uint8_t pos);

#endif /* FATIO_H */